package com.nekonomics
package persistence.repository

import domain.entity.{Address, Order}
import types.{Money, OrderStatus}
import utils.SqlUtil

import doobie.*
import doobie.free.connection.ConnectionIO
import doobie.postgres.implicits.*
import doobie.implicits.*
import zio.{ULayer, ZLayer}

trait OrderRepo {
  def insert(userId: Long, status: OrderStatus, totalPrice: Money, note: Option[String], address: Address): ConnectionIO[Long]

  def findById(id: Long): ConnectionIO[Option[Order]]

  //  def getById(id: Long): ConnectionIO[Order] = findById(id).map(_.get)
  def updateStatusById(orderId: Long, status: OrderStatus): ConnectionIO[Int]

  def listByUnpaid(durationMinute: Int): ConnectionIO[List[Order]]

  def listByShipped(durationMinute: Int): ConnectionIO[List[Order]]
}


object OrderRepo {
  val live: ULayer[OrderRepoLive] = ZLayer.succeed(new OrderRepoLive)
}


class OrderRepoLive extends OrderRepo {

  override def insert(userId: Long, status: OrderStatus, totalPrice: Money, note: Option[String], address: Address): ConnectionIO[Long] = {
    sql"""
         |insert into public.order (
         | user_id, total_price, status,
         | receiver_name, receiver_phone, note, province, city, region, detail_address
         |)
         |values(
         | $userId, $totalPrice, $status,
         | ${address.receiverName}, ${address.receiverPhone}, $note, ${address.province}, ${address.city}, ${address.region}, ${address.detail}
         |)
         |"""
      .stripMargin
      .update
      .withUniqueGeneratedKeys("id")
  }

  override def findById(id: Long): ConnectionIO[Option[Order]] = {
    SqlUtil.selectFromWhereOpt[Order](Some(fr"id=$id"))
      .query[Order]
      .option
  }


  override def updateStatusById(orderId: Long, status: OrderStatus): ConnectionIO[Int] = {
    sql"""
         |update public.order
         |set 
         |  status = $status,
         |  updated_at = now()
         |where id = $orderId
         |
         |""".stripMargin
      .update
      .run
  }


  override def listByUnpaid(durationMinute: Int): ConnectionIO[List[Order]] = {
    val cond = Fragments.and(fr"status = ${OrderStatus.Pending.toString}", fr"now() - created_at > interval"++ Fragment.const(s"'$durationMinute minutes'"))
    SqlUtil.selectFromWhereOpt[Order](Some(cond))
      .query[Order]
      .to[List]
  }


  override def listByShipped(durationMinute: Int): ConnectionIO[List[Order]] = {
    val cond = Fragments.and(fr"status = ${OrderStatus.Shipped.toString}", fr"now() - created_at > interval"++ Fragment.const(s"'$durationMinute minutes'"))
    SqlUtil.selectFromWhereOpt[Order](Some(cond))
      .query[Order]
      .to[List]
  }
}